
// sys
#include <string.h>
#include <stdexcept>
// mine
#include "bit_writer.h"
#include "common.h"
#include "error.h"

namespace msgutils {

namespace {
class RawWriteBuffer : public IWriteBuffer {
 public:
  RawWriteBuffer(uint8_t* buffer, size_t size) 
      : buf_(buffer), size_(size), pos_(0) {}

  virtual void Write(const uint8_t* data, size_t size) {
    if (pos_ + size > size_)
      throw insufficient_buffer_to_write();
    memcpy(buf_ + pos_, data, size);
    pos_ += size;
  }

  virtual size_t Bytes(void) const { return size_ - pos_; }

  virtual size_t BytesWrite(void) const { return pos_; }

 private:
  uint8_t* buf_;
  size_t size_;
  size_t pos_;
};
}

BitWriter::BitWriter(IWriteBuffer* writer_buffer)
    : wbuf_(writer_buffer),
      delete_wbuf_(false) {}

BitWriter::BitWriter(uint8_t* buffer, size_t size) 
    : wbuf_(NULL),
      delete_wbuf_(false) {
  Construct(buffer, size);      
}

BitWriter::~BitWriter(void) {
  if (delete_wbuf_)
    delete wbuf_;
}

void BitWriter::Construct(uint8_t* buffer, size_t size) {
  wbuf_ = new RawWriteBuffer(buffer, size);
  delete_wbuf_ = true;
}

void BitWriter::Write(const char* string) {
  size_t size = strlen(string);
  Write(size);
  wbuf_->Write((const uint8_t*)string, size);
}

namespace {
template<class T>
void EncodeNumberInternal(T val, uint8_t out[], size_t& len) {
  do { 
    uint8_t lower = val & 0x7f;
    val >>= 7;
    if (val)
      lower |= 0x80;
    out[len++] = lower;
  } while (val);
}

}

void BitWriter::EncodeNumber(size_t bits, const void* number_addr) {
  uint8_t buf[10];
  size_t len = 0;

  if (bits == 8)
    EncodeNumberInternal(*(const uint8_t*)number_addr, buf, len);
  else if (bits == 16)
    EncodeNumberInternal(*(const uint16_t*)number_addr, buf, len);
  else if (bits == 32)
    EncodeNumberInternal(*(const uint32_t*)number_addr, buf, len);
  if (bits == 64)
    EncodeNumberInternal(*(const uint64_t*)number_addr, buf, len);

  wbuf_->Write(buf, len);
}

size_t BitWriter::BytesWrite(void) const {
  return wbuf_->BytesWrite();
}

} // namespace msgutils

